/* ctsw.c : context switcher
 */

#include <kernel.h>

 
void _isr_entry(void); //entry point into interrupt service routine
void _timer_entry(void); // entry point into timer interrupt
void _kbd_entry(void);
static void *k_stack; //Location of the kernel's stackint syscall_args_loc;
int syscall_args_loc;

/*
*	Performs context switch into a process pointed to by PCB, and defines
*	_isr_entry() to service an int $63, 63 defined in kernel.h as entry point for a syscall
*	
*/
extern int contextswitch(PCB* curr_proc){
	static unsigned int ESP;
	static int ctsw_ret;
	ESP = (unsigned int)curr_proc->stack;
	__asm __volatile("pushf\n\t"
		"pusha\n\t"
		"movl %%esp, k_stack\n\t"
		"movl %[ESP], %%esp\n\t"
		"movl %[rc], %%eax\n\t"
		"movl %%eax, 28(%%esp)\n\t"
		"popa\n\t"
		"iret\n\t"
	"_kbd_entry:\n\t"
	    "cli\n\t"
	    "pusha\n\t"
	    "movl $0x13, %%eax\n\t"
	    "jmp _common_entry\n\t"
	"_timer_entry:\n\t"
		"cli\n\t"
		"pusha\n\t"
		"movl $0x10, %%eax\n\t"
		"jmp _common_entry\n\t"		
	"_isr_entry:\n\t"
		"cli\n\t"
		"pusha\n\t"
	"_common_entry:\n\t"
		"movl %%esp, %[ESP]\n\t"
		"movl k_stack, %%esp\n\t"
		"movl %%eax, 28(%%esp)\n\t" 
		"movl %%ecx, 24(%%esp)\n\t"
		"popa\n\t"
		"popf\n\t"
		"movl %%eax, %[ctsw_ret]\n\t"
		"movl %%ecx, %[syscall_args_loc]"
	:[ESP] "=m" (ESP), [ctsw_ret] "=m" (ctsw_ret), [rc] "=m" (curr_proc->rc), [syscall_args_loc] "=m" (syscall_args_loc)
	:
	:"%eax"
	);
	curr_proc->stack = (void*)ESP;	
	return ctsw_ret;
}
/*
*	Inserts _isr_entry()'s location into interrupt table and associates it with interrupt 63
*		(student defined interrupt for syscall mechanism as defined in kernel.h)
*/
extern void contextinit(void){
	void (*isr_entry_ptr)(void) = &_isr_entry;
	set_evec(INT_CSWITCH, isr_entry_ptr); 

	// the timer entry point in contextswitch
	void (*isr_timer_ptr)(void) = &_timer_entry;
	set_evec(INT_TIMER, isr_timer_ptr);
	
	// the keyboard device entry point
	//void (*isr_kbd_ptr)(void) = &_kbd_entry;
	
	set_evec(INT_KBD, _kbd_entry);

	// enable the timer interrupt on the PIC	
	initPIT(CLOCK_DIVISOR); // every 100th of a second = 10ms
}
